package com.zsqat.common.valid;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
  实现与该自定义注解关联的验证器
  自定义验证器的实现需要扩展 JSR303 规范提供的接口 javax.validation.ConstraintValidator
  第一个参数: 我们自定义验证注解的名称
  第二个参数: 验证字段的类型
*/
public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {

  // 注解中自定义参数范围必须是基本类型,但是Java 集合只能存放引用数据类型，不能存放基本类型
  private Set<Integer> set = new HashSet<>();
  /**
   * 方法initialize: 对验证器进行实例化，
   * 它必须在验证器的实例在使用之前被调用，
   * 并保证正确初始化验证器，
   * 它的参数是自定义注解，
   * 可以在验证开始前调用注解里的方法，从而获取到一些注解里的参数
   */
  @Override
  public void initialize(ListValue constraintAnnotation) {
    // 获取注解里的参数vals，便于isValid方法判断时使用
    // 方法一： 最简单，最直接
    /*int[] vals = constraintAnnotation.vals();
    for (int val : vals) {
      set.add(val);
    }*/

    /*
    // 方法二： 麻烦，仅仅是为了学习Java8 Stream API
    // 先将int数组转换为数值流
    IntStream stream = Arrays.stream(constraintAnnotation.vals());
    // 流中的元素全部装箱，转换为流 ---->int转为Integer
    Stream<Integer> integerStream = stream.boxed();
    // 将流转换为数组
    Integer[] integers = integerStream.toArray(Integer[]::new);
    set = new HashSet<>(Arrays.asList(integers));
    */

    // 方法三：是对方法二的简化操作
    set = Arrays.stream(constraintAnnotation.vals()).boxed().collect(Collectors.toSet());
  }

  /**
   * 方法isValid: 是进行约束验证的主体方法，
   * 其中 value:  参数代表需要验证的实例，
   *  context:    参数代表约束执行的上下文环境
   */
  @Override
  public boolean isValid(Integer value, ConstraintValidatorContext context) {
    // 判断是否有包含的值
    return set.contains(value);
  }
}
/*
  Java集合如Map、Set、List等所有集合只能存放引用类型数据，它们都是存放引用类型数据的容器，
  不能存放如int、long、float、double等基础类型的数据。

  1. 集合存储对象
     Java集合中实际存放的只是对象的引用，每个集合元素都是一个引用变量，实际内容都放在堆内存或者方法区里面，
     但是基本数据类型是在栈内存上分配空间的，栈上的数据随时就会被收回的。

  2. 基本类型数据如何解决呢？
     可以通过包装类把基本类型转为对象类型，存放引用就可以解决这个问题。
     更方便的，由于有了自动拆箱和装箱功能，基本数据类型和其对应对象（包装类）之间的转换变得很方便，想把基本数据类型存入集合中，直接存就可以了，系统会自动将其装箱成封装类，然后加入到集合当中。

*/